package com.tianji.learning.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import com.tianji.common.autoconfigure.mq.RabbitMqHelper;
import com.tianji.common.constants.MqConstants;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.BooleanUtils;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.constants.RedisConstant;
import com.tianji.learning.domain.vo.SignResultVO;
import com.tianji.learning.listeners.message.SignInMessage;
import com.tianji.learning.service.ISignRecordService;
import io.lettuce.core.StrAlgoArgs;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;

import static com.tianji.learning.constants.RedisConstant.SIGN_KEY_PREFIX;

@Service
@RequiredArgsConstructor
public class SignRecordServiceImpl implements ISignRecordService {
    private final StringRedisTemplate redisTemplate;
    private final RabbitMqHelper mqHelper;
    @Override
    public SignResultVO addSignRecord() {
        LocalDateTime now = LocalDateTime.now();
        int dayOfMonth = now.getDayOfMonth();
        Long userId = UserContext.getUser();
        // 定义格式化器
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(":yyyyMM");

        // 将日期格式化为指定的字符串格式
        String formattedDate = now.format(formatter);
        //1.签到天数
        String key = SIGN_KEY_PREFIX + userId + formattedDate;
        //利用bitset，将签到记录存到redis
        Boolean exits = redisTemplate.opsForValue().setBit(key, dayOfMonth - 1, true);
        if (BooleanUtils.isTrue(exits)) {
            throw new BizIllegalException("不允许重复签到！");
        }
        //2.连续签到天数
        Integer signDays = 0;
        signDays = getSignDays(key, dayOfMonth);

        //3.获得的积分
        int rewardPoints = 0;
        switch (signDays){
            case 7:
                rewardPoints = 10;
                break;
            case 14:
                rewardPoints = 20;
                break;
            case 28:
                rewardPoints = 40;
                break;
        }
        //签到获取的积分由奖励积分和每天签到的一个积分组成
        mqHelper.send(
                MqConstants.Exchange.LEARNING_EXCHANGE,
                MqConstants.Key.SIGN_IN,
                SignInMessage.of(userId,rewardPoints + 1)
        );
        SignResultVO resultVO = new SignResultVO();
        resultVO.setSignDays(signDays);
        resultVO.setRewardPoints(rewardPoints);
        return resultVO;
    }

    @Override
    public Byte[] querySignRecordByMonth() {
        //0.准备参数
        LocalDateTime now = LocalDateTime.now();
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(":yyyyMM");
        String format = now.format(dateTimeFormatter);
        int dayOfMonth = now.getDayOfMonth();
        //1.获取用户id
        Long userId = UserContext.getUser();
        //2.根据key查询redis数据
        String key = SIGN_KEY_PREFIX + userId.toString() + format;
        //2.1 从第0位开始读取，读取连续的dayOfMonth个二进制位（0或1）
        List<Long> bitField = redisTemplate.opsForValue().bitField(key, BitFieldSubCommands.create()
                .get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0));
        //3.处理数据封装返回信息
        Long signValue = 0L;

        if (bitField != null) {
            signValue = bitField.get(0);
        }
        Byte[] bytes = new Byte[dayOfMonth];
        int offset = dayOfMonth - 1;
        while (offset>=0){
            bytes[offset] = (byte) (signValue& 1);
            offset--;
            signValue = signValue >>> 1;
        }
        //00000 00000 00000 00000 01011 11111 0
        return bytes;
    }

    private Integer getSignDays(String key, int dayOfMonth) {
        //BitFieldType.unsigned 意思是取这么多位，并且valueAt从0开始取
        List<Long> bitField = redisTemplate.opsForValue().bitField(key, BitFieldSubCommands.create()
                .get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0));
        if (bitField == null) {
            return 0;
        }
        Long value = bitField.get(0);
        Integer signDays = 0;
        while ((value & 1) == 1){
            signDays++;
            //记得是三个> 因为是无符号
            value = value >>> 1;
        }
        return signDays;
    }
}
